<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8" />
	<title>undescore源码分析</title>
</head>
<body>
<script>
	;(function(){
//		console.log(this);
//		root 获得全局变量
		var root = this;
//		保存全局环境下的_ 变量,到时候进行复制
		var previousUnderscore = root._;
//		console.log(previousUnderscore);
//实现更少的字节和作用域链查找   (可以再浏览数上看原型上有什么内置的方法)
		var ArrayProto = Array.prototype,
			ObjProto = Object.prototype,
			FuncProto = Function.prototype;

		var push = ArrayProto.push,
			slice = ArrayProto.slice,
			toString = ObjProto.toString,
			hasOwnProperty = ObjProto.hasOwnProperty;
//		ECMAScript 5 的原生方法  keys? bind?
		var nativeIsArray = Array.isArray,
			nativeKeys = Object.keys,
			nativeBind = FuncProto.bind,
			nativeCreate = Object.create;
		
//  https://msdn.microsoft.com/zh-cn/library/ff841995(v=vs.94).aspx	
	//	eg  keys 返回对象的可枚举属性和方法的名称      ☆
/*		function getAtrrbute(name,age,index){
			this.name = name,
			this.age = age,
			this.index = index;
			this.getName = function(){
				return this.name;
			}
		}
		var newObj = new getAtrrbute('sm',10,101);
		var arrNum = Object.keys(newObj);
	//	output:
	//		["name", "age", "index", "getName"]
		console.log(arrNum);   */
	
	//  eg  bind 
//对于给定函数，创建具有与原始函数相同的主体的绑定函数。在绑定函数中，this 对象将解析为传入的对象。绑定函数具有指定的初始参数。	
	
	
		//裸体的  Naked 空函数
 		var Ctor = function(){};
// 		暴露_接口
 		var _= function(obj){
 			console.log(this);
 			if(obj instanceof _) return obj;
 			if(!(this instanceof _))return new _(obj);
 			this._wrapped = obj;
 		};
// 		undefined 是string形式的    node.js 扩展
 		if(typeof exports !== 'undefined'){
 			if(typeof module !== 'undefined' && module.exports){
 				exports = module.exports = _;
 			}
 			exports._ = _;
 		}else{
 			root._ = _;
 		}
 		
 		_.VERSION = '1遍';

//??  call apply  
		var optimizeCb = function(func,context,argCount){
			if(context === void 0) return func;
//			 undefined == null  true 
			switch(argCount ==null ?3:argCount){
				case 1:return function(value){
					return func.call(context,value);
				};
				case 2:return function(value,other){

					return func.call(context,value,other);
				};
				case 3: return function (value,index,collection){
					return func.call(context,value,index,collection);
					
				};
				case 4: return function(accumulator,value,index,collection){
					return func.call(context,accumulator,value,index,collection);
				};
			}
			return function(){
				return func.apply(context,arguments);
			};
		};
//		如果传入的是对象,返回的是对象的属性数组
		var cb = function(value,context,argCount){		
			if(value ==null) return _.identity;
			if(_.isFunction(value))return optimizeCb(value,context,argCount);

			if(_.isObject(value))return _.matcher(value);
			return _.property(value);
		}
		_.iteratee = function(value,context){
			return cb(value,context,Infinity);
		}
		
		_.identity = function(value){
			return value;
		}
		
//		创建一个函数，这个函数 返回相同的值 用来作为_.constant的参数
		_.constant = function(value){
			return function(){
				return value;
			}
		}

	// 	内部函数指定
 		var createAssigner = function(keysFunc,undefinedOnly){
 			return function(obj){
 				var length = arguments.length;
 				if(length<2 || obj ==null )return obj;
 				for(var index = 1;index<length;index++){
 					var source = arguments[index],
 						keys = keysFunc(source),
 						l = keys.length;
					for(var i=0;i<l;i++){
						var key = keys[i];
						if(!undefinedOnly || obj[key]=== void 0)obj[key]=source[key];
					}
 				}
 				return obj;
 			};
 		};
 		
	// 	创建新对象
 		var baseCreate = function(prototype){
 			if(!_.isObject(prototype))return {};
	// 	判断原型上有这个函数 (不支持则自己创建 )
 			if(nativeCreate) return nativeCreate(prototype);
 			Ctor.prototype = prototype;
 			var result = new Ctor;
 			Ctor.prototype = null;
 			return result;
 		};
// 		  取属性 
 		var property = function(key){
 			return function(obj){
 				return obj == null ? void 0 : obj[key];
 			};
 		}; 
		_.property = property;
 
 
//	返回true or false  isArrayLike()  通过length 属性来判断
 		var MAX_ARRAY_INDEX = Math.pow(2,53) -1;
 		var getLength= property('length');
 		var isArrayLike = function(collection){
 			var length = getLength(collection);
 			return typeof length == 'number' && length>=0 && length<=MAX_ARRAY_INDEX;
 		};
 			
 		// Collection Functions  集合函数  

    /**
     * each forEach
     * @param 遍历对象 、数组 , 执行函数， 上下文  表示iteratee中的this指向
     * @returns  遍历list
     */
		_.each = _.forEach = function(obj,iteratee,context){
// 			在 74行    判断是否传了上下文
 			iteratee = optimizeCb(iteratee,context);
// 			console.log(iteratee);
 			var i,length;
// 			类数组  在146   取length 属性
 			if(isArrayLike(obj)){
 				for(i=0,length=obj.length;i<length;i++){
 					iteratee(obj[i],i,obj);
 				}
 			}else{
// 				_.keys 在后面的声明的   对象
 				var keys = _.keys(obj);
 				for(i=0,length=keys.length;i<length;i++){
 					iteratee(obj[keys[i]],keys[i],obj);
 				}
 			}
 			return obj;
 		};
 		
    /**
     * map collect
     * @param 遍历对象 、数组 , 执行函数， 上下文
     * @returns 处理过的数组
     */

 		_.map = _.collect = function(obj,iteratee,context){
 			iteratee = cb(iteratee,context);
// 			获得全部属性
 			var keys = !isArrayLike(obj) && _.keys(obj),
 				length =(keys || obj).length,
// 				声明数组长度
 				results = Array(length);
//  优化点 	1	 ☆		
//如果设置了数组长度，那么代码在执行的时候会在初始时就给数组分配一个空间，以后的每次给数组赋值就会更加快捷。
//（因为如果没有指定数组的长度，代码在每次执行赋值的时候会先给数组增加长度，分配空间，降低运行速度。） 
//主要是考虑到后期代码内存的优化问题。
			for(var index =0 ; index<length;index++){
				var currentKey = keys ?keys[index]:index;
				results[index] = iteratee(obj[currentKey],currentKey,obj);
			}
			return results;
 		}
 		
// 		创建一个减少的迭代器    ??
		function createReduce(dir){
			function iterator(obj,iteratee,memo,keys,index,length){
				for(;index>=0 && index<length;index+=dir){
					var currentKey = keys?keys[index]:index;
					memo = iteratee(memo,obj[currentKey],currentKey,obj);
				}
				return memo;
			}
			
			return function(obj,iteratee,memo,context){
				iteratee = optimizeCb(iteratee,context,4);
				var keys = !isArrayLike(obj)&& _.keys(obj),
					length = (keys || obj).length,
					index = dir>0 ?0:length-1;
				if(arguments.length <3){
					memo = obj[keys?keys[index]:index];
					index +=dir;
				}
				return iterator(obj,iteratee,memo,keys,index,length);
			};
		}

//		???
		_.reduce = _.foldl = _.inject = createReduce(1);
		_.reduceRight = _.foldr = createReduce(-1);


//		
    /**
     * 检测  find 可找对象和数组  符号predicate函数的value 
     * 依赖   findIndex  findKey 函数
     * @param 
     * @returns 
     */
		_.find = _.detect = function(obj,predicate,context){
			var key;
			if(isArrayLike(obj)){
				key = _.findIndex(obj,predicate,context);		
			}else{			
				key = _.findKey(obj,predicate,context);
			}
			if(key !== void 0 && key !== -1) return obj[key];
		}
		
		
//		indexFinder 生成器函数
		function createPredicateIndexFinder(dir){
			return function(array,predicate,context){
//				就是给上下文的
				predicate = cb(predicate,context);
				var length = getLength(array);
				var index = dir >0 ?0:length-1;
				for(; index>=0&&index<length;index+=dir){
					if(predicate(array[index],index,array))return index; 
				}
				return -1;
			};
		}		
		_.findIndex = createPredicateIndexFinder(1);

		_.findKey = function(obj,predicate,context){
			predicate = cb(predicate,context);
			var keys = _.keys(obj),key;
			for(var i =0,length=keys.length;i<length;i++){
				key = keys[i];
				console.log(key)
				if(predicate(obj[key],key,obj))return key;
			}
		};

    /**
     * filter 过滤函数  _.each
     * @param 
     * @returns 
     */
		_.filter = _.select = function(obj,predicate,context){
			var  results = [];
			predicate = cb(predicate,context);
			_.each(obj,function(value,index,list){
				if(predicate(value,index,list))results.push(value);
			});
			return results;
		}
    /**
     * reject 和filter 正好相反   ！
     * @param 
     * @returns 
     */		
		_.reject = function	(obj,predicate,context){
			return _.filter(obj,_.negate(cb(predicate)),context);
		}
		
		_.negate = function(predicate){
			return function(){
				return !predicate.apply(this,arguments);
			}
		}
		
    /**
     * every   all   所有的都满足条件返回true 否则false
     * 最新判断的还是传入函数的返回值
     * @param 
     * @returns 
     */	
		_.every = _.all= function(obj,predicate,context){
			predicate = cb(predicate,context);
			console.log(predicate)
			var keys = !isArrayLike(obj)&& _.keys(obj),
				length = (keys || obj).length;
			for(var index =0;index <length;index++){
				var currentKey = keys ? keys[index]:index;
				if(!predicate(obj[currentKey],currentKey,obj))return false;
			}
			return true;
		}
		
    /**
     * some   any   有一个满足条件就返回true 否则false
     * @param  list||obj(每一个)  currentkey obj
     * @returns 
     */			
		
		_.some = _.any = function(obj,predicate,context){
			predicate = cb(predicate,context);
			//	取对象的属性
			var keys = !isArrayLike(obj)&&_.keys(obj),
				length = (keys || obj).length;
			for(var index =0;index<length;index++){
				var currentKey =keys?keys[index]:index;
				if(predicate(obj[currentKey],currentKey,obj))return true;
			}
			return false;
		}
    /**
     * contains  includes   有一个满足条件就返回true 否则false
     * @param  list||obj(每一个)  currentkey obj
     * @returns 
     */	
		
		_.contains = _.includes = _.include = function(obj,item,formIndex,guard){
			if(!isArrayLike(obj)) obj = _.values(obj);
			if(typeof formIndex !='number'|| guard) formIndex =0;
			return _.indexOf(obj,item,formIndex)>=0;
		};
		
		//	检索对象的属性  取对象的值  返回数组
		_.values = function(obj){
			var keys = _.keys(obj),
				length = keys.length,
				values = Array(length),
				i;
			for(var i=0;i<length;i++){
				values[i] = obj[keys[i]];
			}
			return values;
		}
		
		_.indexOf = createIndexFinder(1,_.findIndex,_.sortedIndex);
		_.lastIndexOf = createIndexFinder(-1,_.findLastIndex);
		
    /**
     * indexOf lastIndexOf 通用
     * @param  
     * @returns 
     */			
		function createIndexFinder(dir,predicateFind,sortedIndex){
			return function(array,item,idx){
				var i=0,length = getLength(array);
				if(typeof idx =='number'){
					if(dir>0){
						i=idx>=0?idx:Math.max(idx+length,i);
					}else{
						length=idx>=0?Math.min(idx+1,length):idx+length+1;
					}
				}else if(sortedIndex && idx && length){
					idx = sortedIndex(array,item);
					return array[idx] ===item?idx:-1;
				}
				if(item !==item){
					idx = predicateFind(slice.call(array,i,length),_.isNaN);
					return idx >=0?idx+i:-1;
				}
				for(idx = dir>0?i:length-1;idx>=0&& idx<length;idx+=dir){
					if(array[idx]===item)return idx;
				}
				return -1;
			};
		}

		// 找最小值
		_.sortedIndex = function(array,obj,iteratee,context){
			iteratee = cb(iteratee,context,1);
			console.log(iteratee)
			var value = iteratee(obj);
			var low =0,high = getLength(array);
			while(low<high){
				var mid = Math.floor((low+high)/2);
				if(iteratee(array[mid])<value)low = mid+1;else high =mid;
			}
			return low;
		}
		
//		invoke 调用  原生自带sort 方法
//    在list的每个元素上执行methodName方法
		_.invoke = function(obj,method){
			var args = slice.call(arguments,2);
			var isFunc = _.isFunction(method);
			return _.map(obj,function(value){
				var func = isFunc?method:value[method];
				console.log(func);
				return func ==null?func:func.apply(value,args);
			});
		};
		
//		fetching a property. 抓取属性  把满足key的value 按数组的形式返回
		_.pluck = function(obj,key){
			return _.map(obj,_.property(key));
		}
		
//		依赖了matcher
		_.where = function(obj,attrs){
			return _.filter(obj,_.matcher(attrs));
		}
		
//		又特么依赖了extendOwn
		_.matcher = _.matches = function(attrs){
			attrs = _.extendOwn({},attrs);

			return function(obj){
				return _.isMatch(obj,attrs);
			};
		};			
 		_.keys = function(obj){
			if(!_.isObject(obj)) return [];
			if(nativeKeys)return nativeKeys(obj);
			var keys =[];
			for(var key in obj)if(_.has(obj,key))keys.push(key);
		//	IE<9  
			if(hasEnumBug) collectNonEnumProps(obj,keys);
			return keys;
 		};
		
//		console.log(_.keys);
		_.extendOwn = _.assign = createAssigner(_.keys);
		
		//	告诉你properties中的键和值是否包含在object中。 键值对是否存在obj中
		_.isMatch = function(object,attrs){
			var keys = _.keys(attrs),length = keys.length;
			if(object == null )return !length;
			var obj = Object(object);
			for(var i=0;i<length;i++){
				var key = keys[i];
				if(attrs[key]!==obj[key] || !(key in obj))return false;
			}
			return true;
		}
		
		_.findWhere = function(obj,attrs){
			return _.find(obj,_.matcher(attrs));
		};
		
//		取最大值 可传数组 数组处理函数 对象，对象处理函数  
//		利用两个变量设定无穷小
		_.max = function(obj,iteratee,context){
			var result = -Infinity,lastComputed = -Infinity,
				value,computed;
			if(iteratee==null && obj!=null ){
				obj = isArrayLike(obj)?obj:_.values(obj);
				for(var i=0,length=obj.length;i<length;i++){
					value = obj[i];
					if(value>result){
						result = value;
					}
				}
			}else{
				iteratee = cb(iteratee,context);
				_.each(obj,function(value,index,list){
//					取得传进来的返回值
					computed = iteratee(value,index,list);
	if(computed>lastComputed || computed ===-Infinity && result === -Infinity){
		result = value;
		lastComputed = computed;
	}
				});
			}
			return result;
		};
		
	//	取最小大值 可传数组 数组处理函数 对象，对象处理函数  和max 很相近		
		
		_.min = function(obj,iteratee,context){
			var result = Infinity ,lastComputed = Infinity,
			value,computed;
			if(iteratee ==null && obj != null){
//				取对象的值
				obj = isArrayLike(obj)?obj : _.values(obj);
				for(var i=0,length =obj.length;i<length;i++){
					value =obj[i];
					if(value<result){
						result = value;
					}
				}
			}else{
				iteratee = cb(iteratee,context);
				_.each(obj,function(value,index,list){
					computed = iteratee(value,index,list);
					if(computed<lastComputed ||computed ===Infinity && result ===Infinity ){
						result = value;
						lastComputed = computed;
					}
				});
			}
			return result;
		};
		
//			洗牌算法       ☆
		_.shuffle = function(obj){
			var set = isArrayLike(obj)?obj:_.values(obj);
			var length = set.length,
				shuffled = Array(length);
				for(var index=0,rand;index<length;index++){
					rand = _.random(0,index);
					if(rand !== index) shuffled[index]=shuffled[rand];
					shuffled[rand]= set[index];
				}
				return shuffled;
		};

//		取随机数
		_.random = function(min,max){
			if(max ==null){
				max = min;
				min=0;
			}
			return min+Math.floor(Math.random()*(max-min+1));
		};
		
		
	    /**
	     * 返回随机的个数，不传默认为返回1个。
	     * @param  guard 
	     * @returns 随机的数
	     */
		_.sample = function(obj,n,guard){
			if(n==null || guard){
				if(!isArrayLike(obj))obj = _.values(obj);
				return obj[_.random(obj.length-1)];
			}
			return _.shuffle(obj).slice(0,Math.max(0,n));
		};
		
	    /**
	     * 返回随机的个数，不传默认为返回1个。
	     * @param  guard    待验证
	     * @returns 随机的数
	     */
	    
		_.sortBy = function(obj,iteratee,context){
//			传入上下文
			iteratee = cb(iteratee,context);
			return  _.pluck(_.map(obj,function(value,index,list){
				return {
					value:value,
					index:index,
					criteria:iteratee(value,index,list)
				};
//				sort 是原生方法 返回排序的数组  ？？
			}).sort(function(left,right){
				console.log(left);
				console.log(right);
				var a= left.criteria;
				var b = right.criteria;

				if(a!==b){
					if(a>b || a===void 0)return 1;
					if(a<b || b===void 0)return -1;
				}
				return left.index- right.index;				
			}),'value');
		}
		
//		内部函数用于group by 的组操作
		var group = function(behavior){
			return function(obj,iteratee,context){
				var result = {};
				iteratee = cb(iteratee,context);
				_.each(obj,function(value,index){
					var key = iteratee(value,index,obj);
					behavior(result,value,key);
				});
				return result;
			};
		};
		
//		把一个集合分组为多个集合  如果传属性名,则按属性名进行分组
		_.groupBy = group(function(result,value,key){
			if(_.has(result,key))result[key].push(value);
			else result[key] = [value];
		});
		
//		传属性名,则按属性名进行分组
		_.indexBy = group(function(result,value,key){
			result[key] = value;
		});
		
		_.countBy = group(function(result,value,key){
			if(_.has(result,key))result[key]++;else result[key]=1;
		});
		
//		 返回数组  ☆
		_.toArray = function(obj){
			if(!obj)return [];
			if(_.isArray(obj))return slice.call(obj);
			if(isArrayLike(obj))return _.map(obj,_.identity);
			return _.values(obj);
		}
		
//		判断是否是isArray     ☆
		_.isArray = nativeIsArray || function(obj){
			return toString.call(obj) ==='[object Array]';
		}
		
//		获得长度
		_.size = function(obj){
			if(obj==null)return 0;
			return isArrayLike(obj)?obj.length:_.keys(obj).length;
		}
		
//		切割数组,返回一个条件为真的和一个条件为假的
		_.partition = function(obj,predicate,context){
			predicate = cb(predicate,context);
			var pass=[],fail = [];
			_.each(obj,function(value,key,obj){
				(predicate(value,key,obj)?pass:fail).push(value);
			});
			return[ pass,fail];
		}
		
		
		
		// Array Functions
		// ---------------
		
	    /**
	     * 取数组指定个数  slice 
	     * @param list  截取的个数
	     * @returns array
	     */		
		_.first = _.head = _.take = function(array,n,guard){
			if(array==null)return void 0;
			if(n==null || guard)return array[0];
			return _.initial(array,array.length-n);
		}
		
		_.initial = function(array ,n, guard){
			return slice.call(array,0,Math.max(0,array.length-(n==null || guard ?1:n)));
		};
		
		_.last = function(array,n,guard){
			if(array==null)return void 0;
			if(n==null || guard)return array[array.length-1];
			return _.rest(array,Math.max(0,array.length-n));
		};		
		_.rest = _.tail = _.drop = function(array ,n,guard){
			return slice.call(array,n==null||guard ? 1:n);
		};
//		_.identity 返回值,变为ture or false
		_.compact = function(array){
			return _.filter(array,_.identity);
		}
		
//		递归 ! 要好好理解
		var flatten = function(input,shallow,strict,startIndex){
			var output =[],idx=0;
			for(var i=startIndex || 0,length = getLength(input);i<length;i++){
				var value = input[i];
				if(isArrayLike(value)&& (_.isArray(value)|| _.isArguments(value))){
					if(!shallow)value = flatten(value,shallow,strict);
					var j=0,len=value.length;
					output.length +=len;
					while(j<len){
						output[idx++]= value[j++];
					}
				}else if(!strict){
					output[idx++]= value;
				}
			}
			return output;
		};
		
//		传入shallow true  只递归一层
		_.flatten = function(array,shallow){
			return flatten(array,shallow,false);
		}
		
		
		_.without = function(array){
			return _.difference(array,slice.call(arguments,1));
		};
		
		_.difference = function(array){
			var rest = flatten(arguments,true,true,1);
			return _.filter(array,function(value){
				return !_.contains(rest,value);
			});
		};
		
//		去重
		_.uniq = _.unique = function(array ,isSorted,iteratee,context){
			if(!_.isBoolean(isSorted)){
				context = iteratee;
				iteratee = isSorted;
				isSorted = false;
			}
			if(iteratee!=null) iteratee=cb(iteratee,context);
			var result =[];
			var seen =[];
			for(var i=0,length=getLength(array);i<length;i++){
				var value = array[i],
					computed = iteratee?iteratee(value,i,array):value;
				if(isSorted){
					if(!i || seen !==computed)result.push(value);
					seen = computed;
				}else if(iteratee){
					if(!_.contains(seen,computed)){
						seen.push(computed);
						result.push(value);
					}
				}else if(!_.contains(result,value)){
					result.push(value);
				}
			}
			return result;
		}
		
//		_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
//		按数据原顺序返回 ,并去重
		_.union = function(){
			return _.uniq(flatten(arguments,true,true));
		}
		
//		返回array共同的部分
		_.intersection = function(array){
			var result = [];
			var argsLength = arguments.length;
			for(var i=0,length=getLength(array);i<length;i++){
				var item = array[i];
				if(_.contains(result,item))continue;
				for(var j=1;j<argsLength;j++){
					if(!_.contains(arguments[j],item))break;
				}
				if(j===argsLength)result.push(item);
			}
			return result;
		};
		
		
//		difference    这几个总懵逼
		_.difference = function(array){
			var rest = flatten(arguments,true,true,1);
			return _.filter(array,function(value){
				return !_.contains(rest,value);
			});
		};
		
		_.zip = function(){
			return _.unzip(arguments);
		}
		
		_.unzip = function(array){
			var length = array && _.max(array,getLength).length || 0
			var result = Array(length);
			
			for(var index =0;index<length;index++){
//				_.pluck  抓取属性  把满足key的value
				result[index] = _.pluck(array,index);
			}
			return result;
		}
		
//		将数组转为对象的形式
		_.object = function(list,values){
			var result = {};
			for(var i=0,length=getLength(list);i<length;i++){
				if(values){
					result[list[i]]= values[i];
				}else{
					result[list[i][0]]=list[i][1];
				}
			}
			return result;
		}
		
//		一个用来创建整数灵活编号的列表的函数
		_.range = function(start,stop,step){
			if(stop == null){
				stop=start || 0;
				start =0;
			}
			step = step || 1;
			var length = Math.max(Math.ceil((stop-start)/step),0);
			var range = Array(length);
			
			for(var idx =0; idx<length;idx++,start+=step){
				range[idx] = start;
			}
			return range;
		}
		
		
		//  Function  
		var executeBound = function(sourceFunc,boundFunc,context,callingContext,args){
			if(!(callingContext instanceof boundFunc))return sourceFunc.apply(context,args);
			var self = baseCreate(sourceFunc.prototype);
			var result = sourceFunc.apply(self,args);
			if(_.isObject(result))return result;
			return self;
		}
		
		_.bind = function(func,context){
			if(nativeBind && func.bind===nativeBind)return nativeBind.apply(func,slice.call(arguments,1));
//			浏览器的  抛出错误   行为 。 
			if(!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
			var args = slice.call(arguments,2);
			var bound = function(){
				return executeBound(func,bound,context,this,args.concat(slice.call(arguments)));
			};
			return bound;
		}
		
		_.partial = function(func){
			var boundArgs = slice.call(arguments,1);
			var bound = function(){
				var position=0,length=boundArgs.length;
				var args = Array(length);
				for(var i=0;i<length;i++){
					args[i]=boundArgs[i]===_?arguments[position++]:boundArgs[i];
				}
				while (position<arguments.length)args.push(arguments[position++]);
				return executeBound(func,bound,this,this,args);
			};
			return bound;
		}
		
		_.bindAll = function(obj){
			var i,length = arguments.length,key;
			if(length<=1)throw new Error('bindAll must be passed function names');
			for(i=1;i<length;i++){
				key = arguments[i];
				obj[key]= _.bind(obj[key],obj);
			}
			return obj;
		};
		
		_.memoize = function(func,hasher){
			var memoize = function(key){
				var cache = memoize.cache;
				var address = ''+(hasher?hasher.apply(this,arguments):key);
				
				if(!_.has(cache,address))cache[address]=func.apply(this,arguments);
				return cache[address];
			};
			
			memoize.cache ={};
			return memoize;
		}
		
		_.delay = function (func,wait){
			var args = slice.call(arguments,2); 
			return setTimeout(function(){
				return func.apply(null,args);
			},wait);
		};
		
//		延迟函数     延迟调用function直到当前调用栈清空为止
		_.defer = _.partial(_.delay,_,1);
		
//		像节流阀一样的函数   ？
		_.throttle = function(func,wait,options){
			var context,args,result;
			var timeout = null;
			var previous =0;
			if(!options) options = {};
			var later = function(){
				previous = options.leading ===false?0:_.now();
				timeout=null;
				result = func.apply(context,args);
				if(!timeout)context = args =null;
			};
			return function(){
				var now = _.now();
				if(!previous && options.leading ===false )previous=now;
				var remaining = wait - (now-previous);
				context = this;
				args = arguments;
				if(remaining <=0 || remaining>wait){
					if(timeout){
						clearTimeout(timeout);
						timeout = null;
					}
					previous = now;
					result = func.apply(context,args);
					if(!timeout)context = args = null;
				}else if(!timeout && options.trailing!==false){
					timeout = setTimeout(later,remaining);
				}
				return result;
			}
			
		}
		
//		取时间  返回毫秒数
		_.now = Date.now || function(){
			return new Date().getTime();
		}
		
		_.debounce = function(func,wait,immediate){
			var timeout,args,context,timestamp,result;
//			later 函数可以验证一下
			var later = function(){
				var last = _.now() - timestamp;
				if(last <wait && last >=0 ){
					timeout = setTimeout(later,wait-last);
				}else{
					timeout = null ;
					if(!immediate){
						result = func.apply(context,args);
						if(!timeout)context = args = null;
					}
				}
			};
			
			return function(){
				context = this;
				args = arguments;
				timestamp = _.now();
				var callNow = immediate && !timeout;
				if(!timeout)timeout = setTimeout(later,wait);
				if(callNow){
					result = func.apply(context,args);
					context = args = null;
				}
				return result;
			};
		};

		
		_.wrap = function(func,wrapper){
			return _.partial(wrapper,func);
		}
		
		_.negate = function(predicate){
			return function(){
				return !predicate.apply(this,arguments);
			}
		}
		
//		 在数学里, 把函数 f(), g(), 和 h() 组合起来可以得到复合函数 f(g(h()))。
		
		_.compose = function(){
			var args = arguments;
			var start = args.length -1;
			return function(){
				var i= start;
				var result = args[start].apply(this,arguments);
				while(i--) result = args[i].call(this,result);
				return result;
			}
		}
		//  ?
		_.after = function(times,func){
			return function(){
				if(--times<1){
					return func.apply(this,arguments);
				}
			}
		}
		//  ?
		_.before = function(times,func){
			var memo;
			return function(){
				if(--times>0){
					memo = func.apply(this,arguments);
				}
				if(times<=1)func = null;
				return memo;
			};
		};
		
//		只执行一次的函数
		_.once = _.partial(_.before,2);
		
//		Object Functions
		
//		ie 9 下的处理   for in 的兼容处理            ☆
		var hasEnumBug = !{toString:null}.propertyIsEnumerable('toString');
		var nonEnumerableProps = ['valueOf','isPrototypeOf','toString','propertyIsEnumerable','hasOwnProperty','toLocaleString'];
//		ie 9
		function collectNonEnumProps(obj,keys){
			var nonEnumIdx = nonEnumerableProps.length;
			var constructor = obj.constructor;
			var proto = (_.isFunction(constructor)&& constructor.prototype) || ObjProto;
		
			var prop = 'constructor';
			if(_.has(obj,prop)&& !_.contains(keys,prop)) keys.push(prop);
		
			while(nonEnumIdx--){
				prop = nonEnumerableProps[nonEnumIdx];
				if(prop in obj && obj[prop] !== proto[prop] && !_.contains(keys,prop)){
					keys.push(prop);					
				}
			}
		}
		
//		取属性非继承来的 (_.has() 来判断)  ,有原生的情况下使用原生  Object.keys      ☆
		_.keys = function(obj){
			if(!_.isObject(obj))return [];
			if(nativeKeys)return nativeKeys(obj);
			var keys = [];
			
			for(var key in obj) if(_.has(obj,key)) keys.push(key);
//			  IE＜　９　
			if(hasEnumBug) collectNonEnumProps(obj,keys);
			return keys;			
		};
		
//		原型上增加的部分
		_.allKeys = function(obj){
			if(!_.isObject(obj)) return [];
			var keys = [];
			for(var key in obj) keys.push(key);
			
			if(hasEnumBug) collectNonEnumProps(obj,keys);
			return keys;			
		};
		
//		对对象进行预处理
		_.mapObject = function(obj,iteratee,context){
			iteratee = cb(iteratee,context);
			var keys = _.keys(obj),
				length = keys.length,
				results = {},
				currentKey;
			for(var index =0;index<length;index++){
				currentKey = keys[index];
				results[currentKey] = iteratee(obj[currentKey],currentKey,obj);
			}
			return results;
		}
		
//		把一个对象转变为一个[key, value]形式的数组。
		_.pairs = function(obj){
//			获得所有的属性
			var keys = _.keys(obj);
			var length = keys.length;
//			pairs 数组
			var pairs = Array(length);
			
			for(var i=0;i<length;i++){
				pairs[i] = [keys[i],obj[keys[i]]];
			}
			return pairs;			
		}
		
//		反转  键值反  
		_.invert = function(obj){
			var result = {};
			var keys = _.keys(obj);
			for(var i=0,length = keys.length;i<length;i++){
				result[obj[keys[i]]] = keys[i];
			}
			return result;
		}
		
//		取得所有的方法名     eg  _.functions(_)
		_.functions = _.methods = function(obj){
			var names = [];
			for(var key in obj){
				if(_.isFunction(obj[key]))names.push(key);
			}
			return names.sort();
		}
		
//		_.extend({name: 'moe'}, {age: 50});
//		=> {name: 'moe', age: 50} 
//		复制source对象中的所有属性,返回一个对象
		_.extend = createAssigner(_.allKeys);
//		只复制自己的属性
		_.extendOwn = _.assign = createAssigner(_.keys);
			
		_.pick = function(object,oiteratee,context){
			var result = {},
				obj = object,
				iteratee,
				keys;
			if(obj == null)return result;
			if(_.isFunction(oiteratee)){
				keys = _.allKeys(obj);
				iteratee = optimizeCb(oiteratee,context);
			}else{
//				递归函数
				keys = flatten(arguments,false,false,1);
				iteratee = function(value,key,obj){return key in obj;};
				obj = Object(obj);
			}
			for(var i=0,length= keys.length;i<length;i++){
				var key = keys[i];
				var value = obj[key];
				if(iteratee(value,key,obj))result[key] = value;
			}
			return result;
		};
		
//		和pick  前面相反
		_.omit = function(obj,iteratee,context){
			if(_.isFunction(iteratee)){
				iteratee = _.negate(iteratee);
			}else {
				var keys = _.map(flatten(arguments,false,false,1),String);
				iteratee = function(value,key){
					return !_.contains(keys,key);
				};
			}
			return _.pick(obj,iteratee,context);
		}
		
		
//		添加  多种方法     ☆  ☆
		
		_.each(['Arguments','Function','String','Number','Date','RegExp','Error'],function(name){
			_['is'+name]= function(obj){
				return toString.call(obj)=== '[object '+name+']' ;
			};
		});

//			用defaults对象填充object 中的undefined属性
		_.defaults = createAssigner(_.allKeys,true);
		
		_.create = function(prototype,props){
			var result = baseCreate(prototype);
			console.log(result)
//			扩展属性
			if(props) _.extendOwn(result,props);
			return result;
		}
		
		_.clone = function(obj){
			if(!_.isObject(obj))return obj;
			return _.isArray(obj)?obj.slice():_.extend({},obj);
		};
		
//		?
		_.tap = function(obj,interceptor){
			interceptor(obj);
			return obj;
		}
		
//		判断obj 里头是否能匹配到    attrs  
		_.isMatch = function(object,attrs){
			var keys = _.keys(attrs),length = keys.length;
			if(object == null ) return !length;
			var obj = Object(object);
			for(var i=0;i<length;i++){
				var key = keys[i];
				if(attrs[key] !== obj[key] || !(key in obj))return false;
			}
			return true;
		}
		
		var eq = function(a,b,aStack,bStack){
			if(a===b)return a!==0 || 1 / a === 1 / b;
			if(a==null || b == null )return a===b;
			if(a instanceof _) a= a._wrapped;
			if(b instanceof _) b = b._wrapped;
			
			var className = toString.call(a);
			if(className !== toString.call(b))return false;
			
			switch(className){
				case '[object RegExp]':
				case '[object String]':
					return ''+a === ''+b;
				case '[object Number]':
					if(+a !== +a) return +b !== +b;
					return +a === 0 ? 1/ +a === 1 /b :+a===+b;
				case '[object Date]':
				case '[object Boolean]':
				
				return +a === +b;
			}
			
			var areArrays = className === '[object Array]';
			if(!areArrays){
				if(typeof a != 'object'|| typeof b !='object')return false;
				
				var aCtor = a.constructor,bCtor = b.constructor;
				if(aCtor !== bCtor && !(_.isFunction(aCtor)&& aCtor instanceof aCtor &&
									_.isFunction(bCtor)&& bCtor instanceof bCtor)
									&& ('constructor' in a && 'constructor' in b)){
					return false;						
				}
			}

		
			aStack = aStack || [];
			bStack = bStack || [];
			var length = aStack.length;
	//		用的比较多的是减减  据说这样的效率更高
			while(length --){
				if(aStack[length]===a)return bStack[length]===b;
			}
			
			aStack.push(a);
			bStack.push(b);
			
			if(areArrays){
				length = a.length;
				if(length !== b.length)return false;
				while(length--){
					if(!eq(a[length],b[length],aStack,bStack))return false;
				}
			}else{
				var keys = _.keys(a),key;
				length = keys.length;
				if(_.keys(b).length !== length)return false;
				while(length--){
					key = keys[length];
					if(!(_.has(b,key)&& eq(a[key],b[key],aStack,bStack)))return false;
				}
			}
			
			aStack.pop();
			bStack.pop();
			return true;
		};
		
		
//		var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
//		var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
//		stooge == clone;
//		=> false
//		_.isEqual(stooge, clone);
//		=> true
		
		_.isEqual = function(a,b){
			return eq(a,b);
		}
		
//		其实是通过判断length 来实现的
		_.isEmpty = function(obj){
			if(obj == null )return true;
			if(isArrayLike(obj)&& (_.isArray(obj)|| _.isString(obj)|| _.isArguments(obj)))return obj.length ===0;
			return _.keys(obj).length ===0;
		};
		
//		☆	 nodeType 来判断	
		_.isElement = function(obj){
			return !!(obj && obj.nodeType ===1 );
		};
		
		
		
		_.isNaN = function(obj){
			return _.isNumber(obj) && obj !== +obj;
		};
		
		
		if(!_.isArguments(arguments)){
			_.isArguments = function(obj){
				return _.has(obj,'callee');
			}
		}
		
//		是否是有限的数字  检测某个值是不是正常数值
		_.isFinite = function(obj){
			return isFinite(obj) && !isNaN(parseFloat(obj));
		};
		
		
		
//		布尔值
		_.isBoolean = function(obj){
			return obj===true||obj===false||toString.call(obj)==='[object Boolean]';
		}
		
		_.isNull = function(obj){
			return obj === null;
		}
		
//		判断是否是undefined   
		_.isUndefined = function(obj){
			return obj === void 0;
		}
		
 		_.has = function(obj,key){
 			return obj!=null && hasOwnProperty.call(obj,key); 
 		};
 		
 		_.isObject = function(obj){
 			var type = typeof obj;
			// 	0 || 1&& 1   1    0 || 1&& 0   0	
 			return type ==='function'|| type==='object'&& !!obj
 		}
 		
// 		Utility Functions  实用的函数
 		
// 		释放控制权  underscore 
 		_.noConflict = function(){
//		previousUnderscore  再一进来就指定好了
 			root._ = previousUnderscore;
 			return this;
 		}
 	
 		_.noop = function(){};
 		
//	  	  var property = function(key) {
//		    return function(obj) {
//		      return obj == null ? void 0 : obj[key];
//		    };
//		  };
 		
 		_.property = property;
 		
 		_.propertyOf = function(obj){
 			return obj == null ?function(){}:function(key){
 				return obj[key];
 			};
 		};
// 		调用给定的迭代函数n次,生成一个返回值的数组。		
 		_.times = function(n,iteratee,context){
 			var accum = Array(Math.max(0,n));
 			iteratee = optimizeCb(iteratee,context,1);
 			for(var i=0;i<n;i++) accum[i]= iteratee(i);
 			return accum;
 		}
 		
 		
		_.now  = Date.now || function(){
			return new Date().getTime();
		}
// 		    这个很重要  ☆    转义特殊字符
 		var escapeMap = {
 			'&':'&amp;',
 			'<':'&lt;',
 			'>':'&gt;',
 			'"':'&quot;',
 			"'":'&#x27;',
 			'`':"&#x60;"
 		};
 		
// 		_.invert 对象键值 反转    ☆
 		var unescapeMap = _.invert(escapeMap);
 		
 		var createEscaper = function(map){
 			var escaper = function(match){
 				return map[match];
 			};
 			var source = '(?:'+_.keys(map).join('|')+')';
 			var testRegexp = RegExp(source);
 			var replaceRegexp = RegExp(source,'g');
 			
 			return function(string){
 				string = string==null?'':''+string;
 				return testRegexp.test(string)?string.replace(replaceRegexp,escaper):string;
 			};
 		};
 		
 		_.escape = createEscaper(escapeMap);
 		_.unescape = createEscaper(unescapeMap);
 		
 		
 		
		//		没啥用
   		_.result = function(object,property,fallback){
   			var value = object == null?void 0:object[property];
   			if(value === void 0){
   				value = fallback;
   			}
   			return _.isFunction(value)?value.call(object):value;
   		};
   		
// 		生成一个唯一的 id
		var idCounter =0;
		_.uniqueId = function(prefix){
			var id = ++idCounter+'';
			return prefix?prefix + id:id;
		};
 		
// 		?
 		
// 		!!!!
 		_.templateSetting = {
 			evaluate :/<%([\s\S]+？)%>/g,
 			interpolate:/<%=([\s\S]+?)%>/g,
 			escape :/<%-([\s\S]+?)%>/g
 		};
 		
 		var noMatch = /(.)^/;
 		var escapes = {
 			"'":"'",
 			'\\':'\\',
 			'\r':'r',
 			'\n':'n',
 			'\u2028':'u2028',
 			'\u2029':'u2029'
 		};
 		
 		var escapeChar = function(match){
 			return '\\'+escapes[match];
 		}
 		
 		_.template = function(text,settings,oldSettings){
 			if(!settings && oldSettings)settings = oldSettings;
 			settings = _.defaults({},settings,_.templateSettings);
 		
 			var matcher = RegExp([
 				(settings.escape || noMatch).source,
 				(settings.interpolate|| noMatch).source,
 				(settings.evaluate || noMatch).source
 			].join('|')+'|$','g');
 			
 			var index = 0;
 			var source = "__p+='";
 			text.replace(matcher,function(match,escape,interpolate,evaluate,offset){
 				source += text.slice(index,offset).replace(escaper,escapeChar);
 				index = offset + match.length;
 				
 				if(escape){
 					source +="'+\n((__t("+escape+"))==null?'':_.escape(__t))+\n'";
 				}else if(interpolate){
 					source +="'+\n((__t=("+interpolate+"))==null?'':__t)+\n'";
 				}else if(evaluate){
 					source +="';\n"+evaluate+"\n__p+='";
 				}
 				return match;
 			})
 			
 			if(!settings.variable)source = 'with(obj||{}){\n'+source+'\n}';
 			
 			source = "var __t,__p='',__j=Array.prototype.join,"+
 					"print=function(){__p+=__j.call(arguments,'');};\n"+
 					source +'return __p;\n';
 			
 			try{
 				var render = new Function(settings.variable || 'obj','_',source);
 			}catch(e){
 				e.source = source;
 				throw e;
 			}
 			var template = function(data){
 				return render.call(this,data,_);
 			};
 			var argument = settings.variable || 'obj';
 			template.source = 'function('+argument+'){\n'+source+'}';
 			
 			return template;			
 		}
 		
 		
 		_.chain = function(obj){
 			var instance = _(obj);
 			instance._chain = true;
 			return instance;
 		}
 		
// 		OOP       ???
 		
 		var result = function(instance,obj){
 			return instance._chain ? _(obj).chain():obj;
 		};
 		
 		_.mixin = function(obj){
 			_.each(_.functions(obj),function(name){
 				var func = _[name] = obj[name];
 				_.prototype[name]= function(){
 					var args = [this._wrapped];
 					push.apply(args,arguments);
 					return result(this,func.apply(_,args));
 				};
 			});
 		};
 		
 		_.mixin(_);
 		
// 		添加数组的包装器
 		_.each(['concat','join','slice'],function(name){
 			var method = ArrayProto[name];
 			_.prototype[name] = function(){
 				return result(this,method.apply(this._wrapped,arguments));
 			};
 		});
 		
 		_.prototype.value = function(){
 			return this._wrapped;
 		}
 		
 		_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
 		
 		_.prototype.toString = function(){
 			return ''+this._wrapped;
 		};
 		
// 		解决old v8 bug
		// Int8Array   8 位整数值的类型化数组。内容将初始化为 0

		if(typeof /./ !='function' && typeof Int8Array !='object'){		
 			_.isFunction = function(obj){
 				return typeof obj==='function' || false;
 			};		
		}
		
		if(typeof define === 'function'&& define.amd){
			define('underscore',[],function(){
				return _;
			});
		}
		
//		☆  1
		function myIsNaN(value){
			return typeof value ==='number'&& isNaN(value);
		}
//		2
		for(var key in person){
			if(person.hasOwnProperty(key)){
				console.log(key);
			}
		}		
//		3
		
//		模板引擎的原理
//		var o ={
//			name:'Alice'
//		};
//		var p=[];
//		with(o){
//			p.push('Hello',name,'!');
//		};
//		p.join('');
//		
//		delete arr[1]  会产生空位   空位表示没有这个元素
//		数组的某个位置是空位，与某个位置是undefined，是不一样的。
//如果是空位，使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历，
//空位都会被跳过
		
		function isObject(value) {
		  return value === Object(value);
		}		
		isObject([]) // true
		isObject(true) // false
		
//		Object.prototype.toString.call(2) // "[object Number]"
//		Object.prototype.toString.call('') // "[object String]"
//		Object.prototype.toString.call(true) // "[object Boolean]"
//		Object.prototype.toString.call(undefined) // "[object Undefined]"
//		Object.prototype.toString.call(null) // "[object Null]"
//		Object.prototype.toString.call(Math) // "[object Math]"
//		Object.prototype.toString.call({}) // "[object Object]"
//		Object.prototype.toString.call([]) // "[object Array]"
		
		
//		var type = function (o){
//		  var s = Object.prototype.toString.call(o);
//		  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
//		};
//		
//		type({}); // "object"
//		type([]); // "array"
//		type(5); // "number"
//		type(null); // "null"
//		type(); // "undefined"
//		type(/abcd/); // "regex"
//		type(new Date()); // "date"
		
//		slice方法的一个重要应用，是将类似数组的对象转为真正的数组。
//		Array.prototype.slice.call(document.querySelectorAll("div"));
		
		
		
		
//		11.15 ko
 		
	//	传入全局变量  window or node环境
	}.call(this));
</script>
</body>
</html>